﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;

namespace Pixysoft.Framework.Logs.Core
{
    /// <summary>
    /// 线程安全写入日志 记录写入长度 自动打包
    /// </summary>
    class CoreLoggerWriter : IDisposable
    {
        bool isDisposed = false;

        bool isClosed = false;

        long filelength = 0;

        private static readonly ReaderWriterLock locker = new ReaderWriterLock();

        ILoggerSetting setting = null;

        StreamWriter writer = null;

        public CoreLoggerWriter(ILoggerSetting setting, StreamWriter writer)
        {
            this.setting = setting;

            this.writer = writer;
        }


        public void Write(string value)
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                writer.Write(value);

                writer.Flush();

                filelength += value.Length;

                if (filelength < setting.LogCapacity)
                    return;

                AutoBackup(setting.GetLogFilePath(), setting.Filename, StringHelper.default_log_postfix);

                filelength = 0;
            }
            catch (ObjectDisposedException ex)
            {
                this.TryOpen();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        public Encoding Encoding
        {
            get
            {
                return writer.Encoding;
            }
        }


        /// <summary>
        /// 线程安全
        /// </summary>
        public void Flush()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                writer.Flush();
            }
            catch (ObjectDisposedException ex)
            {
                this.TryOpen();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        /// <summary>
        /// 线程安全 关闭writer 但是仍然会被重用
        /// </summary>
        public void Close()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                TryClose();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }

        /// <summary>
        /// 线程安全 关闭删除writer 不会再重用
        /// </summary>
        public void Dispose()
        {
            locker.AcquireWriterLock(Timeout.Infinite);

            try
            {
                writer.Dispose();
            }
            finally
            {
                locker.ReleaseWriterLock();
            }

            this.writer = null;

            this.setting = null;

            this.isDisposed = true;
        }

        /// <summary>
        /// 非 线程安全
        /// </summary>
        public bool IsDisposed
        {
            get
            {
                return this.isDisposed;
            }
        }

        /// <summary>
        /// 是否已经关闭
        /// </summary>
        public bool IsClosed
        {
            get { return isClosed; }
        }


        private bool AutoBackup(string path, string filename, string postfix)
        {
            string pathfilename = GetFileName(path, filename, postfix);

            if (!File.Exists(pathfilename))
                return false;

            //关闭流

            this.writer.Flush();

            this.writer.Close();

            this.writer.Dispose();


            //备份文件

            string backfilename = null;

            while (true)
            {
                backfilename = GetBakFileName(path, filename, postfix);

                if (File.Exists(backfilename))
                    continue;

                break;
            }

            //http://bytes.com/topic/net/answers/486309-rename-file-c

            //09-11-30 使用moveto，不要copyto

            try
            {
                FileInfo info = new FileInfo(pathfilename);

                info.MoveTo(backfilename);
            }
            catch (Exception ex)
            {
                return false;
            }

            //重建流

            FileStream sw = File.Open(pathfilename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);

            StreamWriter newWriter = new StreamWriter(sw, Encoding.UTF8);

            this.writer = newWriter;

            return true;
        }

        private string GetFileName(string path, string filename, string postfix)
        {
            if (!path.EndsWith(@"\"))
                return path + @"\" + filename + postfix;
            else
                return path + filename + postfix;
        }

        private string GetBakFileName(string path, string filename, string postfix)
        {
            if (!path.EndsWith(@"\"))
                return path + @"\" + filename + "." + Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTimeSequence() + postfix;
            else
                return path + filename + "." + Pixysoft.Tools.GlobalTimer.Instance.GetGlobalTimeSequence() + postfix;
        }


        public void Lock()
        {
            locker.AcquireWriterLock(Timeout.Infinite);
        }

        public void Unlock()
        {
            locker.ReleaseWriterLock();
        }

        /// <summary>
        /// 非 线程安全
        /// </summary>
        public void TryOpen()
        {
            if (!Directory.Exists(setting.GetLogFilePath()))
            {
                Directory.CreateDirectory(setting.GetLogFilePath());
            }

            string path = setting.GetLogFilePath();

            string filename = setting.Filename;

            string postfix = StringHelper.default_log_postfix;

            string pathfilename = GetFileName(path, filename, postfix);

            FileStream sw = File.Open(pathfilename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);

            StreamWriter newWriter = new StreamWriter(sw, Encoding.UTF8);

            this.writer = newWriter;
        }

        /// <summary>
        /// 非 线程安全
        /// </summary>
        public void TryClose()
        {
            this.writer.Close();

            this.writer.Dispose();

            this.writer = null;

            this.isClosed = true;
        }

        /// <summary>
        /// 对文件进行备份 如果文件不存在 则跳开
        /// 非 线程安全
        /// </summary>
        /// <param name="path"></param>
        /// <param name="filename"></param>
        /// <returns></returns>
        public bool TryBackup()
        {
            string path = setting.GetLogFilePath();

            string filename = setting.Filename;

            string postfix = StringHelper.default_log_postfix;

            return this.AutoBackup(path, filename, postfix);
        }
    }
}
